
#include "MemoryPool.hpp"
#include <cstdlib>

MemoryPool::MemoryPool(size_t size) 
{
	mySize = size;
	myMemory = malloc(size);
	if(myMemory == NULL) throw AllocationError();
}

MemoryPool::~MemoryPool() 
{
	free(myMemory);
}

void * MemoryPool::GetMemory(size_t size) 
{
	void * memory = FindFreeMemory(size);
	if(memory != NULL) {
		myPointers.insert(std::pair<void *, size_t>(memory, size));
		return memory;
	} else {
		throw InsufficentMemoryError();
	}
}

void MemoryPool::FreeMemory(void * memory) 
{
	std::map<void *, size_t>::iterator it = myPointers.find(memory);
	if(it == myPointers.end()) throw InvalidPointerError();
	myPointers.erase(it);
}

size_t MemoryPool::GetSize() const 
{
	return mySize;
}

bool MemoryPool::CanAllocate(size_t size) const 
{
	return FindFreeMemory(size) != NULL;
}

void * MemoryPool::FindFreeMemory(size_t size) const 
{
	std::map<void *, size_t>::const_iterator it = myPointers.begin(), end = myPointers.end(), last = it;
	void * memory = NULL; size_t pointersSize = myPointers.size();

	if(pointersSize > 0) 
	{
		for(it++; it != end; it++, last++) 
		{
			if(((size_t)it->first) - ((size_t)last->first + last->second) > size) 
			{
				memory = (void *) (((size_t)last->first) + last->second + 1);
				break;
			}
		}
	}

	if(pointersSize == 0) 
	{
		memory = myMemory;
	} 
	else if(it == end) 
	{
		it--;
		memory = (void *) (((size_t)it->first) + it->second + 1);
		if((size_t)memory + size > (size_t)myMemory + mySize) memory = NULL;
	}

	return memory;
}